home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / xstat.lha / XStat.mod < prev   
Text File  |  1992-09-04  |  46KB  |  1,516 lines

  1. (***************************************************************************
  2.  :Program.    XStat
  3.  :Author.     Jⁿrgen Weinelt
  4.  :Address.    Zur Kanzel 1, D-8783 Hammelburg, Germany
  5.  :Address.    USENET: uunet.uu.net!cbmvax!cbmehq!cbmger!imart!hcast!jow
  6.  :Version.    $VER: XStat 1.10 (04-Sep-92)
  7.  :Copyright.  Freeware
  8.  :Language.   Modula-2
  9.  :Translator. M2Amiga V4.097d
  10.  :History.    1.00 initial release
  11.  :History.    1.01 bug fix: used to guru when xfer = 0 bytes
  12.  :History.    1.02 added peak cps rating
  13.  :History.    1.03 added monthly statistics
  14.  :History.    1.04 switched to DosL.ReadArgs(), fixed nasty current date
  15.  :History.         bug with WB2.1 (problems with locale and "TODAY")
  16.  :History.    1.05 added per host statistics
  17.  :History.    1.06 integrated "per host" statistics into "HOSTNAME" option
  18.  :History.         (pattern matching using dos.library)
  19.  :History.    1.07 bug fix: linked list of Statistics-records was not
  20.  :History.         sorted correctly
  21.  :History.    1.08 added STDERR option; replaced all "°"'s with "av."
  22.  :History.    1.09 added "LOCAL" and "IGNORE" to XStat.data
  23.  :History.    1.10 added multiple names feature to XStat.data N-lines
  24.  :History.         fixed a small bug in the NI/NO feature
  25.  :Contents.   Extract statistics from UUCiCo "Xferstat" logfile. Requires
  26.  :Contents.   UUCiCo 1.15c (or later) by Andrew "Charly" Kopp
  27.  **************************************************************************)
  28.  
  29.  
  30.  
  31. MODULE XStat;
  32. (*$ StackChk:=FALSE *)
  33. (*$ CaseChk:=FALSE *)
  34. (*$ Volatile:=FALSE *)
  35.  
  36.  
  37.  
  38. (*$ DEFINE BETA:=FALSE *)
  39.  
  40.  
  41.  
  42. IMPORT Arts;
  43. IMPORT ASCII;
  44. IMPORT Break;
  45. IMPORT DosD;
  46. IMPORT DosL;
  47. IMPORT ExecD;
  48. IMPORT ExecL;
  49. IMPORT InOut;
  50. IMPORT R;
  51. IMPORT RealConversions;
  52. IMPORT RealInOut;
  53. IMPORT String;
  54.  
  55. IMPORT SYSTEM;
  56.  
  57.  
  58.  
  59. CONST
  60.  strlen=100;
  61.  
  62.  dmult=24*60*60;
  63.  hmult=60*60;
  64.  mmult=60;
  65.  smult=1;
  66.  dsun=0*dmult;
  67.  dmon=1*dmult;
  68.  dtue=2*dmult;
  69.  dwed=3*dmult;
  70.  dthu=4*dmult;
  71.  dfri=5*dmult;
  72.  dsat=6*dmult;
  73.  
  74.  defxstatdata="UULIB:XStat.data";
  75.  defxferstat="UUSPOOL:XferStat";
  76.  
  77.  vers="XStat 1.10" (*$ IF BETA *) +" *BETA*" (*$ ENDIF *) ;
  78.  version="$VER: "+vers+" (04-Sep-92)";
  79.  argTemplate="FILE/K,DATA/K,V=VERBOSE/S,M=MONTH/K,NI=NOINCOM/S,NO=NOOUTGO/S,"+
  80.              "Q=QUIET/S,H=HOSTNAME/K,FD=FROMDATE/K,TD=TODATE/K,SE=STDERR/S";
  81.  
  82.  extendedhelp="\nXStat "+argTemplate+"\n"+
  83.              "\n DATA      XStat.data file\t\t  def="+defxstatdata+
  84.              "\n FILE      Xferstat file\t\t  def="+defxferstat+
  85.              "\n VERBOSE   verbose mode switch  \t  def=off"+
  86.              "\n MONTH     monthly, override -f/-t\t  def=off (format MMM-YY)"+
  87.              "\n NOINCOM   don't evaluate incoming calls  def=off"+
  88.              "\n NOOUTGO   don't evaluate outgoing calls  def=off"+
  89.              "\n QUIET     suppress non-fatal errors\t  def=off"+
  90.              "\n HOSTNAME  evaluate calls to host\t  def=all"+
  91.              "\n FROMDATE  ignore calls before date\t  def=01-JAN-78"+
  92.              "\n TODATE    ignore calls after date\t  def=current date/time"+
  93.              "\n STDERR    send error messages to StdErr  def=off\n\n";
  94.  
  95.  
  96.  
  97. TYPE
  98.  StrPtr=POINTER TO ARRAY[0..255] OF CHAR;
  99.  
  100.  DosArgs=
  101.    RECORD
  102.      file: StrPtr;
  103.      data: StrPtr;
  104.      verb: LONGINT;
  105.      month: StrPtr;
  106.      noin: LONGINT;
  107.      noout: LONGINT;
  108.      quiet: LONGINT;
  109.      host: StrPtr;
  110.      fdate: StrPtr;
  111.      tdate: StrPtr;
  112.      stderr: LONGINT;
  113.    END;
  114.  
  115.  StringT=ARRAY[0..strlen] OF CHAR;
  116.  StringTPtr=POINTER TO StringT;
  117.  
  118.  
  119.  
  120.  File=
  121.    RECORD
  122.      fh: DosD.FileHandlePtr;
  123.      eof: BOOLEAN;
  124.    END;
  125.  
  126.  
  127.  
  128.  CostPtr=POINTER TO Cost;
  129.  Cost=
  130.    RECORD
  131.      next: CostPtr;
  132.      start: LONGINT;        (* start time for mpu and unit                  *)
  133.      stop: LONGINT;         (* end time for mpu and unit                    *)
  134.      mpu: REAL;             (* price per unit                               *)
  135.      unit: REAL;            (* unit duration                                *)
  136.    END;
  137.  
  138.  
  139.  
  140.  XSDataPtr=POINTER TO XSData;
  141.  XSData=
  142.    RECORD
  143.      next: XSDataPtr;
  144.      host: StringT;       (* host name                                    *)
  145.      local: BOOLEAN;      (* count transmissions, no cost, no units       *)
  146.      ignore: BOOLEAN;     (* ignore this host entirely                    *)
  147.      double: BOOLEAN;     (* if true, don't free the attached costlist    *)
  148.      costlist: CostPtr;   (* list of Cost records                         *)
  149.    END;
  150.  
  151.  
  152.  
  153.  Connect=
  154.    RECORD
  155.      host: StringT;                 (* host name *)
  156.      callout: BOOLEAN;              (* TRUE: outgoing call *)
  157.      start: LONGINT;                (* session start time *)
  158.      stop: LONGINT;                 (* session end time *)
  159.      sdate: DosD.Date;              (* session start time in dos format *)
  160.      inbb: LONGINT;                 (* brutto bytes in *)
  161.      outbb: LONGINT;                (* brutto bytes out *)
  162.      inbn: LONGINT;                 (* netto bytes in *)
  163.      outbn: LONGINT;                (* netto bytes out *)
  164.      cost: REAL;                    (* connection phone costs *)
  165.      units: LONGINT;                (* phone units consumed *)
  166.      seconds: LONGINT;              (* online time in seconds *)
  167.      local: BOOLEAN;                (* was local -> no cost, no units *)
  168.      ignore: BOOLEAN;               (* do not count this at all *)
  169.    END;
  170.  
  171.  
  172.  
  173.   StatisticsPtr=POINTER TO Statistics;
  174.   Statistics=
  175.     RECORD
  176.       next: StatisticsPtr;
  177.       name: StringT;
  178.       in: BOOLEAN;
  179.       connects: LONGINT;
  180.       inbb: LONGINT;
  181.       outbb: LONGINT;
  182.       inbn: LONGINT;
  183.       outbn: LONGINT;
  184.       cost: REAL;
  185.       units: LONGINT;
  186.       seconds: LONGINT;
  187.       bpeak: LONGINT;
  188.       npeak: LONGINT;
  189.       localconnects: LONGINT;
  190.       ignoreconnects: LONGINT;
  191.     END;
  192.  
  193.  
  194.  
  195.   Args=
  196.     RECORD
  197.       xstatdata: StringT;           (* xstat.data name and path;          *)
  198.       xferstat: StringT;            (* xferstat name and path;            *)
  199.       verbose: BOOLEAN;             (* lotsa output;              ; off   *)
  200.       from: DosD.Date;              (* from date;                 ; zero  *)
  201.       to: DosD.Date;                (* to date;                   ; TODAY *)
  202.       host: ARRAY[0..400] OF CHAR;  (* host name pattern;         ; all   *)
  203.       in: BOOLEAN;                  (* process incoming calls;    ; on    *)
  204.       out: BOOLEAN;                 (* process outgoing calls;    ; on    *)
  205.       quiet: BOOLEAN;               (* suppress non-fatal errors; ; off   *)
  206.       monthly: BOOLEAN;             (* monthly mode;              ; off   *)
  207.       perhost: BOOLEAN;             (* perhost mode;              ; off   *)
  208.       stderr: BOOLEAN;              (* stderr mode;               ; off   *)
  209.    END;
  210.  
  211.  
  212.  
  213. VAR
  214.   inf: File;
  215.   ln1,ln2,temp: StringT;
  216.   log: Connect;
  217.   totali,totalo: Statistics;
  218.   xsroot: XSDataPtr;
  219.   args: Args;
  220.   currency: StringT;
  221.   rdargsPtr: DosD.RDArgsPtr;
  222.   statroot: StatisticsPtr;
  223.   index: INTEGER;
  224.   stdErr: DosD.FileHandlePtr;
  225.  
  226.  
  227.  
  228. (*---------------------------------------------------------------------------
  229.   FreeCostChain: FreeMem() a linked list of "Cost" records
  230.   ---------------------------------------------------------------------------*)
  231. PROCEDURE FreeCostChain(d:CostPtr);
  232.   VAR
  233.     n: CostPtr;
  234.   BEGIN
  235.     REPEAT
  236.       n:=d^.next;
  237.       ExecL.FreeMem(d,SIZE(d^));
  238.       d:=n;
  239.     UNTIL n=NIL;
  240.   END FreeCostChain;
  241.  
  242.  
  243.  
  244. (*---------------------------------------------------------------------------
  245.   FreeStat: FreeMem() a linked list of "Statistics" records
  246.   ---------------------------------------------------------------------------*)
  247. PROCEDURE FreeStat(s: StatisticsPtr);
  248.   VAR
  249.     n: StatisticsPtr;
  250.   BEGIN
  251.     (* some memory may be lost if an error occurs while the XStat.data file  *)
  252.     (* is being parsed... that's because the n^.tmp field is not checked     *)
  253.     (* here. the problem is, checking n^.tmp might cause a "memory freed     *)
  254.     (* twice" guru under certain conditions. losing a few bytes seems like   *)
  255.     (* the smaller evil in this case.                                        *)
  256.     REPEAT
  257.       n:=s^.next;
  258.       ExecL.FreeMem(s,SIZE(s^));
  259.       s:=n;
  260.     UNTIL n=NIL;
  261.   END FreeStat;
  262.  
  263.  
  264.  
  265. (*---------------------------------------------------------------------------
  266.   FreeXData: FreeMem() a linked list of "XSData" records, freeing the
  267.              attached "Cost" record lists as well (if double=FALSE)
  268.   ---------------------------------------------------------------------------*)
  269. PROCEDURE FreeXData(d: XSDataPtr);
  270.   VAR
  271.     n: XSDataPtr;
  272.   BEGIN
  273.     REPEAT
  274.       n:=d^.next;
  275.       IF (d^.costlist#NIL) AND (NOT d^.double) THEN
  276.         FreeCostChain(d^.costlist);
  277.       END;
  278.       ExecL.FreeMem(d,SIZE(d^));
  279.       d:=n;
  280.     UNTIL n=NIL;
  281.   END FreeXData;
  282.  
  283.  
  284.  
  285. (*---------------------------------------------------------------------------
  286.   MyError: Display error messages, terminate program if necessary
  287.            redirect errors to STDERR if requested by new 1.08 cli switch
  288.   ---------------------------------------------------------------------------*)
  289. (*$ CopyDyn:=FALSE *)
  290. PROCEDURE MyError(x1,x2,x3: ARRAY OF CHAR; fatal: BOOLEAN);
  291.   VAR
  292.     lidummy: LONGINT;
  293.   BEGIN
  294.     IF stdErr#NIL THEN
  295.       IF fatal THEN
  296.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("FATAL ERROR "),12);
  297.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x1),String.Length(x1));
  298.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
  299.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x2),String.Length(x2));
  300.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
  301.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x3),String.Length(x3));
  302.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\n"),2);
  303.         Arts.Exit(DosD.fail);
  304.       ELSIF (NOT args.quiet) OR ((x1[0]="F") & (x1[1]="A") & (x1[2]="T")) THEN
  305.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x1),String.Length(x1));
  306.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
  307.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x2),String.Length(x2));
  308.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\o"),1);
  309.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR(x3),String.Length(x3));
  310.         lidummy:=DosL.Write(stdErr,SYSTEM.ADR("\n\n"),2);
  311.       END;
  312.     ELSE
  313.       IF fatal THEN
  314.         InOut.WriteString("FATAL ERROR ");
  315.         InOut.WriteString(x1); InOut.WriteLn;
  316.         InOut.WriteString(x2); InOut.WriteLn;
  317.         InOut.WriteString(x3); InOut.WriteLn;
  318.         InOut.WriteLn;
  319.         Arts.Exit(DosD.fail);
  320.       ELSIF (NOT args.quiet) OR ((x1[0]="F") & (x1[1]="A") & (x1[2]="T")) THEN
  321.         InOut.WriteString(x1); InOut.WriteLn;
  322.         InOut.WriteString(x2); InOut.WriteLn;
  323.         InOut.WriteString(x3); InOut.WriteLn;
  324.         InOut.WriteLn;
  325.       END;
  326.     END;
  327.   END MyError;
  328.  
  329.  
  330.  
  331. (*---------------------------------------------------------------------------
  332.   GetHead: Remove first char from a string, return it in char variable
  333.   ---------------------------------------------------------------------------*)
  334. PROCEDURE GetHead(VAR s: StringT; VAR c: CHAR);
  335.   BEGIN
  336.     c:=s[0];
  337.     String.DeleteChar(s,0);
  338.   END GetHead;
  339.  
  340.  
  341.  
  342. (*---------------------------------------------------------------------------
  343.   KillSpaces: Remove leading spaces from a string
  344.   ---------------------------------------------------------------------------*)
  345. PROCEDURE KillSpaces(VAR l1: StringT);
  346.   VAR
  347.     c: CHAR;
  348.   BEGIN
  349.     WHILE l1[0]=" " DO
  350.       GetHead(l1,c);
  351.       Break.TestBreak();
  352.     END;
  353.   END KillSpaces;
  354.  
  355.  
  356.  
  357. (*---------------------------------------------------------------------------
  358.   Get: Remove first "word" from a string. "Word" means "a sequence of
  359.        non-spaces, terminated by at least one space". Return this word
  360.        as a separate string
  361.   ---------------------------------------------------------------------------*)
  362. PROCEDURE Get(VAR s1,s2: StringT): BOOLEAN;
  363.   VAR
  364.     c: CHAR;
  365.   BEGIN
  366.     s2[0]:=ASCII.nul;
  367.     KillSpaces(s1);
  368.     GetHead(s1,c);
  369.     WHILE (c#" ") AND (c#ASCII.nul) DO
  370.       Break.TestBreak();
  371.       String.ConcatChar(s2,c);
  372.       GetHead(s1,c);
  373.     END;
  374.     RETURN String.Length(s2)>0;
  375.   END Get;
  376.  
  377.  
  378.  
  379. (*---------------------------------------------------------------------------
  380.   ConvDate: Convert ASCII representations of date and time to Dos format
  381.   ---------------------------------------------------------------------------*)
  382. (*$ CopyDyn:=FALSE *)
  383. PROCEDURE ConvDate(ds,ts: ARRAY OF CHAR; VAR dat: DosD.Date): BOOLEAN;
  384.   VAR
  385.     dt: DosD.DateTime;
  386.     res: LONGINT;
  387.   BEGIN
  388.     dt.format:=DosD.formatDOS;
  389.     dt.flags:=DosD.DateTimeFlagSet{};
  390.     dt.strDate:=SYSTEM.ADR(ds);
  391.     dt.strTime:=SYSTEM.ADR(ts);
  392.     res:=DosL.StrToDate(SYSTEM.ADR(dt));
  393.     dat:=dt.date;
  394.     RETURN res=0;
  395.   END ConvDate;
  396.  
  397.  
  398.  
  399. (*---------------------------------------------------------------------------
  400.   ParseDosArgs: Parse argument string and set "args" record accordingly
  401.   ---------------------------------------------------------------------------*)
  402. PROCEDURE ParseDosArgs(VAR s: StringT);
  403.   VAR
  404.     ts: StringT;
  405.     dargs: DosArgs;
  406.     rc: SYSTEM.ADDRESS;
  407.     pres: LONGINT;
  408.   BEGIN
  409.     rdargsPtr:=DosL.AllocDosObject(DosD.dosRdArgs,NIL);
  410.     IF rdargsPtr=NIL THEN
  411.       MyError("in command line parser",
  412.               "not enough memory",
  413.               "can't parse command line arguments",TRUE);
  414.     END;
  415.  
  416.     rdargsPtr^.source.buffer:=SYSTEM.ADR(s);
  417.     rdargsPtr^.source.length:=String.Length(s);
  418.     rdargsPtr^.source.curChr:=0;
  419.     rdargsPtr^.daList:=0;
  420.     rdargsPtr^.buffer:=NIL;
  421.     rdargsPtr^.bufSiz:=0;
  422.     rdargsPtr^.extHelp:=SYSTEM.ADR(extendedhelp);
  423.     rdargsPtr^.flags:=DosD.RDAFlagSet{};
  424.  
  425.     IF DosL.ReadArgs(SYSTEM.ADR(argTemplate),SYSTEM.ADR(dargs),rdargsPtr)=NIL THEN
  426.       MyError("in command line parser","illegal command line",s,TRUE);
  427.     END;
  428.  
  429.     IF dargs.file#NIL THEN
  430.       String.Copy(args.xferstat,dargs.file^);
  431.     END;
  432.  
  433.     IF dargs.data#NIL THEN
  434.       String.Copy(args.xstatdata,dargs.data^);
  435.     END;
  436.  
  437.     args.verbose:=(dargs.verb#0);
  438.  
  439.     IF dargs.fdate#NIL THEN
  440.       IF ConvDate(dargs.fdate^,"00:00:00",args.from) THEN
  441.         MyError("in command line","illegal FDATE",dargs.fdate^,TRUE);
  442.       END;
  443.     END;
  444.  
  445.     IF dargs.tdate#NIL THEN
  446.       IF ConvDate(dargs.tdate^,"23:59:59",args.to) THEN
  447.         MyError("in command line","illegal TDATE",dargs.tdate^,TRUE);
  448.       END;
  449.     END;
  450.  
  451.     IF dargs.host#NIL THEN
  452.       pres:=DosL.ParsePatternNoCase(dargs.host,
  453.                                     SYSTEM.ADR(args.host),
  454.                                     SIZE(args.host)-2); (* just being cautious :-) *)
  455.       IF pres<0 THEN
  456.         MyError("in command line","pattern in HOST too complex",dargs.host^,TRUE);
  457.       END;
  458.       args.perhost:=(pres=1);
  459.     END;
  460.  
  461.     IF args.in THEN
  462.       args.in:=(dargs.noin=0);
  463.     END;
  464.  
  465.     IF args.out THEN
  466.       args.out:=(dargs.noout=0);
  467.     END;
  468.  
  469.     args.quiet:=args.quiet OR (dargs.quiet#0);
  470.  
  471.     args.stderr:=(dargs.stderr#0);
  472.  
  473.     IF dargs.month#NIL THEN
  474.       ts:="01-";
  475.       String.Concat(ts,dargs.month^);
  476.       IF ConvDate(ts,"00:00:00",args.from) THEN
  477.         MyError("in command line","illegal MONTH",dargs.month^,TRUE);
  478.       END;
  479.       ts:="31-";
  480.       String.Concat(ts,dargs.month^);
  481.       IF ConvDate(ts,"23:59:59",args.to) THEN
  482.         ts:="30-";
  483.         String.Concat(ts,dargs.month^);
  484.         IF ConvDate(ts,"23:59:59",args.to) THEN
  485.           ts:="29-";
  486.           String.Concat(ts,dargs.month^);
  487.           IF ConvDate(ts,"23:59:59",args.to) THEN
  488.             ts:="28-";
  489.             String.Concat(ts,dargs.month^);
  490.             IF ConvDate(ts,"23:59:59",args.to) THEN
  491.               MyError("in command line","illegal MONTH",dargs.month^,TRUE);
  492.             END;
  493.           END;
  494.         END;
  495.       END;
  496.       args.monthly:=TRUE;
  497.     END;
  498.     IF rdargsPtr#NIL THEN
  499.       DosL.FreeArgs(rdargsPtr);
  500.       DosL.FreeDosObject(DosD.dosRdArgs,rdargsPtr);
  501.       rdargsPtr:=NIL;
  502.     END;
  503.   END ParseDosArgs;
  504.  
  505.  
  506.  
  507. (*---------------------------------------------------------------------------
  508.   PresetArgs: Set default values for "args" record
  509.   ---------------------------------------------------------------------------*)
  510. PROCEDURE PresetArgs();
  511.   VAR
  512.    bdummy: BOOLEAN;
  513.   BEGIN
  514.     args.xstatdata:=defxstatdata;
  515.     args.xferstat:=defxferstat;
  516.     args.verbose:=FALSE;
  517.     bdummy:=ConvDate("01-JAN-78","00:00:00",args.from);
  518.     DosL.DateStamp(SYSTEM.ADR(args.to));
  519.     args.host[0]:=ASCII.nul;
  520.     args.in:=TRUE;
  521.     args.out:=TRUE;
  522.     args.quiet:=FALSE;
  523.     args.monthly:=FALSE;
  524.   END PresetArgs;
  525.  
  526.  
  527.  
  528. (*---------------------------------------------------------------------------
  529.   GetLine: Read a line from the input file
  530.   ---------------------------------------------------------------------------*)
  531. PROCEDURE GetLine(VAR line: StringT);
  532.   VAR
  533.     res: LONGINT;
  534.   BEGIN
  535.     IF inf.fh#NIL THEN
  536.       res:=DosL.FGets(inf.fh,SYSTEM.ADR(line),strlen);
  537.       inf.eof:=(res=0) AND (DosL.IoErr()=0);
  538.     END;
  539.     IF (String.Length(line)>0) AND (line[String.Length(line)-1]=ASCII.lf) THEN
  540.       line[String.Length(line)-1]:=ASCII.nul;
  541.     END;
  542.   END GetLine;
  543.  
  544.  
  545.  
  546. (*---------------------------------------------------------------------------
  547.   GetLog: Read a two-line log entry from Xferstat
  548.   ---------------------------------------------------------------------------*)
  549. PROCEDURE GetLog(VAR l1,l2: StringT);
  550.   BEGIN
  551.     l1[0]:=ASCII.nul;
  552.     l2[0]:=ASCII.nul;
  553.     REPEAT
  554.       Break.TestBreak();
  555.       GetLine(l1);
  556.     UNTIL (inf.eof) OR (l1[0]="<") OR (l1[0]=">");
  557.     IF NOT inf.eof THEN
  558.       GetLine(l2);
  559.     END;
  560.   END GetLog;
  561.  
  562.  
  563.  
  564. (*---------------------------------------------------------------------------
  565.   Skip: Skip the first "word" of a string; refer to "Get()" for an
  566.         explanation ot the term "word"
  567.   ---------------------------------------------------------------------------*)
  568. PROCEDURE Skip(VAR s: StringT);
  569.   VAR
  570.     dum: BOOLEAN;
  571.     x: StringT;
  572.   BEGIN
  573.     dum:=Get(s,x);
  574.   END Skip;
  575.  
  576.  
  577.  
  578. (*---------------------------------------------------------------------------
  579.   GetLNum: Get the first "word" of a string, and convert it to a LONGINT
  580.            (if possible). Refer to "Get()" for an explanation of the
  581.            term "word"
  582.   ---------------------------------------------------------------------------*)
  583. PROCEDURE GetLNum(VAR s: StringT; VAR x: LONGINT): BOOLEAN;
  584.   VAR
  585.     sn: StringT;
  586.     res: LONGINT;
  587.   BEGIN
  588.     IF Get(s,sn) THEN
  589.       res:=DosL.StrToLong(SYSTEM.ADR(sn),x);
  590.       RETURN res>0;
  591.     END;
  592.     RETURN FALSE;
  593.   END GetLNum;
  594.  
  595.  
  596.  
  597. (*---------------------------------------------------------------------------
  598.   GetNum: Call "GetLNum()" and make sure the result is an INTEGER
  599.   ---------------------------------------------------------------------------*)
  600. PROCEDURE GetNum(VAR s: StringT; VAR x: INTEGER): BOOLEAN;
  601.   VAR
  602.     l: LONGINT;
  603.   BEGIN
  604.     IF GetLNum(s,l) THEN
  605.       IF l>MAX(INTEGER) THEN
  606.         x:=0;
  607.       ELSE
  608.         x:=INTEGER(l);
  609.         RETURN TRUE;
  610.       END;
  611.     ELSE
  612.       x:=0;
  613.     END;
  614.     RETURN FALSE;
  615.   END GetNum;
  616.  
  617.  
  618.  
  619. (*---------------------------------------------------------------------------
  620.   GetReal: Get the first "word" of a string, and convert it to a REAL
  621.            (if possible). Refer to "Get()" for an explanation of the
  622.            term "word"
  623.   ---------------------------------------------------------------------------*)
  624. PROCEDURE GetReal(VAR s: StringT; VAR x: REAL): BOOLEAN;
  625.   VAR
  626.     sn: StringT;
  627.     err: BOOLEAN;
  628.   BEGIN
  629.     IF Get(s,sn) THEN
  630.       RealConversions.StrToReal(sn,x,err);
  631.       RETURN NOT err;
  632.     END;
  633.     RETURN FALSE;
  634.   END GetReal;
  635.  
  636.  
  637.  
  638. (*---------------------------------------------------------------------------
  639.   GetTime: Get the first two "words" of a string, and convert them to
  640.            Dos format date and time (if possible). Refer to "Get()" for an
  641.            explanation of the term "word"
  642.   ---------------------------------------------------------------------------*)
  643. PROCEDURE GetTime(VAR s: StringT; VAR t: LONGINT; VAR dos: DosD.Date): BOOLEAN;
  644.   VAR
  645.     ds,ts: StringT;
  646.     dt: DosD.DateTime;
  647.   BEGIN
  648.     IF Get(s,ds) THEN
  649.       IF Get(s,ts) THEN
  650.         dt.format:=DosD.formatCDN;
  651.         dt.flags:=DosD.DateTimeFlagSet{};
  652.         dt.strDate:=SYSTEM.ADR(ds);
  653.         dt.strTime:=SYSTEM.ADR(ts);
  654.  
  655.         IF DosL.StrToDate(SYSTEM.ADR(dt))#0 THEN
  656.           dos:=dt.date;
  657.           t:=(dos.days MOD 7)*dmult+(dos.minute*mmult)+(dos.tick DIV 50);
  658.           RETURN TRUE;
  659.         END;
  660.       END;
  661.     END;
  662.     RETURN FALSE;
  663.   END GetTime;
  664.  
  665.  
  666.  
  667. (*---------------------------------------------------------------------------
  668.   TestXStatHeader: Check if the input file's header is "H XSTAT DATA"
  669.   ---------------------------------------------------------------------------*)
  670. PROCEDURE TestXStatHeader();
  671.  VAR
  672.   s1,s,t: StringT;
  673.   c: CHAR;
  674.  BEGIN
  675.   GetLine(s);
  676.   s1:=s;
  677.   GetHead(s,c);
  678.   IF c="H" THEN
  679.    IF Get(s,t) OR (String.Compare(t,"XSTAT")#0) THEN
  680.     IF NOT (Get(s,t) OR (String.Compare(t,"DATA")#0)) THEN
  681.      MyError("in XStat.data header",
  682.              s1,"keyword DATA not found",TRUE);
  683.     END;
  684.    ELSE
  685.     MyError("in XStat.data header",
  686.             s1,"keyword XSTAT not found",TRUE);
  687.    END;
  688.   ELSE
  689.    MyError("in XStat.data header",
  690.            s1,"header identifier H not found",TRUE);
  691.   END;
  692.  END TestXStatHeader;
  693.  
  694.  
  695.  
  696. (*---------------------------------------------------------------------------
  697.   ParseXSDataNEntry: Parse a "host name" line from XStat.data
  698.   ---------------------------------------------------------------------------*)
  699. PROCEDURE ParseXSDataNEntry(VAR s,s1: StringT; VAR ccxsd: XSDataPtr);
  700.   VAR
  701.     cxsd: XSDataPtr;
  702.     xsd: XSData;
  703.     t: StringT;
  704.     first: BOOLEAN;
  705.   BEGIN
  706.     IF (ccxsd#NIL) AND (ccxsd^.costlist=NIL) AND (NOT ccxsd^.local) AND (NOT ccxsd^.ignore) THEN
  707.       MyError("in XStat.data",
  708.               "N-line without subsequent C-lines for",
  709.               ccxsd^.host,TRUE);
  710.     END;
  711.  
  712.     first:=TRUE;
  713.     IF Get(s,t) THEN
  714.       REPEAT
  715.         xsd.next:=NIL;
  716.         xsd.host:=t;
  717.         xsd.costlist:=NIL;
  718.         xsd.local:=FALSE;
  719.         xsd.ignore:=FALSE;
  720.         xsd.double:=FALSE;
  721.         cxsd:=xsroot;
  722.         IF xsroot#NIL THEN
  723.           WHILE cxsd^.next#NIL DO
  724.             Break.TestBreak();
  725.             cxsd:=cxsd^.next;
  726.           END;
  727.           cxsd^.next:=ExecL.AllocMem(SIZE(xsd),ExecD.MemReqSet{});
  728.           IF cxsd^.next=NIL THEN
  729.             MyError("","not enough memory","",TRUE);
  730.           END;
  731.           cxsd:=cxsd^.next;
  732.           IF first THEN
  733.             first:=FALSE;
  734.             ccxsd:=cxsd;
  735.           ELSE
  736.             xsd.double:=TRUE;
  737.           END;
  738.         ELSE
  739.           xsroot:=ExecL.AllocMem(SIZE(xsd),ExecD.MemReqSet{});
  740.           IF xsroot=NIL THEN
  741.             MyError("","not enough memory","",TRUE);
  742.           END;
  743.           cxsd:=xsroot;
  744.           ccxsd:=cxsd;
  745.           first:=FALSE;
  746.         END;
  747.         cxsd^:=xsd;
  748.       UNTIL NOT Get(s,t);
  749.     ELSE
  750.       MyError("in XStat.data",
  751.               s1,"no host name found in N-line",TRUE);
  752.     END;
  753.   END ParseXSDataNEntry;
  754.  
  755.  
  756.  
  757. (*---------------------------------------------------------------------------
  758.   ParseXSDataCEntry: Parse a "cost info" line from XStat.data
  759.   ---------------------------------------------------------------------------*)
  760. PROCEDURE ParseXSDataCEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
  761.   VAR
  762.     t,t1: StringT;
  763.     cost: Cost;
  764.     ccp: CostPtr;
  765.     temp: INTEGER;
  766.     c0{R.D7},c1{R.D6}: CHAR;
  767.   BEGIN
  768.     IF cxsd=NIL THEN
  769.       MyError("in XStat.data",s1,"C-line without corresponding N-line",TRUE);
  770.     END;
  771.  
  772.     cost.next:=NIL;
  773.     cost.stop:=7*dmult-1;
  774.  
  775.     IF NOT Get(s,t) THEN
  776.       MyError("in XStat.data",s1,"illegal or missing start time in C-line",TRUE);
  777.     END;
  778.  
  779.     IF String.Compare(t,"LOCAL")=0 THEN
  780.       IF cxsd^.costlist#NIL THEN
  781.         MyError("in XStat.data",s1,"LOCAL must be the only C-line for the host",TRUE);
  782.       ELSIF cxsd^.local#FALSE THEN
  783.         MyError("in XStat.data",s1,"duplicate LOCAL after N-line",TRUE);
  784.       ELSIF cxsd^.ignore#FALSE THEN
  785.         MyError("in XStat.data",s1,"LOCAL and IGNORE are mutually exclusive",TRUE);
  786.       ELSE
  787.         cxsd^.local:=TRUE;
  788.       END;
  789.  
  790.     ELSIF String.Compare(t,"IGNORE")=0 THEN
  791.       IF cxsd^.costlist#NIL THEN
  792.         MyError("in XStat.data",s1,"IGNORE must be the only C-line for the host",TRUE);
  793.       ELSIF cxsd^.ignore#FALSE THEN
  794.         MyError("in XStat.data",s1,"duplicate IGNORE after N-line",TRUE);
  795.       ELSIF cxsd^.local#FALSE THEN
  796.         MyError("in XStat.data",s1,"LOCAL and IGNORE are mutually exclusive",TRUE);
  797.       ELSE
  798.         cxsd^.ignore:=TRUE;
  799.       END;
  800.  
  801.     ELSIF (t[2]="-") AND (t[5]=":") AND (t[8]=":") AND (String.Length(t)=11) THEN
  802.       c0:=t[0]; c1:=t[1];
  803.       IF (c0="S") AND (c1="U") THEN
  804.         cost.start:=dsun;
  805.       ELSIF (c0="M") AND (c1="O") THEN
  806.         cost.start:=dmon;
  807.       ELSIF (c0="T") AND (c1="U") THEN
  808.         cost.start:=dtue;
  809.       ELSIF (c0="W") AND (c1="E") THEN
  810.         cost.start:=dwed;
  811.       ELSIF (c0="T") AND (c1="H") THEN
  812.         cost.start:=dthu;
  813.       ELSIF (c0="F") AND (c1="R") THEN
  814.         cost.start:=dfri;
  815.       ELSIF (c0="S") AND (c1="A") THEN
  816.         cost.start:=dsat;
  817.       ELSE
  818.         MyError("in XStat.data",s1,"illegal day of week in start time in C-line",TRUE);
  819.       END;
  820.  
  821.       t[0]:=" "; t[1]:=" "; t[2]:=" "; t[5]:=" "; t[8]:=" ";
  822.  
  823.       IF NOT GetNum(t,temp) THEN
  824.         MyError("in XStat.data",s1,"illegal 'hours' part in start time in C-line",TRUE);
  825.       END;
  826.       cost.start:=cost.start+hmult*LONGINT(temp);
  827.  
  828.       IF NOT GetNum(t,temp) THEN
  829.         MyError("in XStat.data",s1,"illegal 'minutes' part in start time in C-line",TRUE);
  830.       END;
  831.       cost.start:=cost.start+mmult*LONGINT(temp);
  832.  
  833.       IF NOT GetNum(t,temp) THEN
  834.         MyError("in XStat.data",s1,"illegal 'seconds' part in start time in C-line",TRUE);
  835.       END;
  836.       cost.start:=cost.start+smult*LONGINT(temp);
  837.  
  838.       IF NOT GetReal(s,cost.unit) THEN
  839.         MyError("in XStat.data",s1,"illegal 'seconds per unit' in C-line",TRUE);
  840.       END;
  841.  
  842.       IF NOT GetReal(s,cost.mpu) THEN
  843.         MyError("in XStat.data",s1,"illegal 'money per unit' in C-line",TRUE);
  844.       END;
  845.  
  846.       IF (cxsd^.local) OR (cxsd^.ignore) THEN
  847.         MyError("in XStat.data",s1,"no more C-lines allowed after LOCAL or IGNORE",TRUE);
  848.       END;
  849.  
  850.       ccp:=cxsd^.costlist;
  851.       IF ccp#NIL THEN
  852.         WHILE ccp^.next#NIL DO
  853.           Break.TestBreak();
  854.           ccp:=ccp^.next;
  855.         END;
  856.         ccp^.next:=ExecL.AllocMem(SIZE(Cost),ExecD.MemReqSet{});
  857.         IF ccp^.next=NIL THEN
  858.           MyError("","not enough memory","",TRUE);
  859.         END;
  860.         ccp^.next^:=cost;
  861.         ccp^.stop:=cost.start-1;
  862.         IF ccp^.stop<ccp^.start THEN
  863.           MyError("in XStat.data","connect start time sequence error",
  864.                   "(you probably swapped some C-lines, or you left out an N-line)",TRUE);
  865.         END;
  866.       ELSE
  867.         cxsd^.costlist:=ExecL.AllocMem(SIZE(Cost),ExecD.MemReqSet{});
  868.         IF cxsd^.costlist=NIL THEN
  869.           MyError("","not enough memory","",TRUE);
  870.         END;
  871.         cxsd^.costlist^:=cost;
  872.         IF cost.start#0 THEN
  873.           MyError("in XStat.data",s1,"start time in first C-line must be SU-00:00:00",TRUE);
  874.         END;
  875.       END;
  876.     ELSE
  877.       MyError("in XStat.data",s1,"illegal or missing start time in C-line",TRUE);
  878.     END;
  879.  
  880.     (* cxsd will always be #NIL here... *)
  881.     WHILE cxsd^.next#NIL DO
  882.       (* copy costlistPtr to next^.costlistPtr *)
  883.       cxsd^.next^.costlist:=cxsd^.costlist;
  884.       cxsd^.next^.local:=cxsd^.local;
  885.       cxsd^.next^.ignore:=cxsd^.ignore;
  886.       (* advance cxsd to cxsd#.next; note: cxsd is a VAR parameter, so this *)
  887.       (* will only happen for the first C-record found... from then on,     *)
  888.       (* this costlistPtr copying will not be done again.                   *)
  889.       cxsd:=cxsd^.next;
  890.     END;
  891.   END ParseXSDataCEntry;
  892.  
  893.  
  894.  
  895. (*---------------------------------------------------------------------------
  896.   ParseXSDataSEntry: Parse a "currency sign info" line from XStat.data
  897.   ---------------------------------------------------------------------------*)
  898. PROCEDURE ParseXSDataSEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
  899.  VAR
  900.   t: StringT;
  901.  BEGIN
  902.   IF (cxsd#NIL) THEN
  903.    MyError("in XStat.data",
  904.            s1,"S-line after the first connection cost record",TRUE);
  905.   END;
  906.  
  907.   IF Get(s,t) THEN
  908.    IF currency[0]=ASCII.nul THEN
  909.     currency:=t;
  910.     IF currency[0]#" " THEN
  911.      String.Insert(currency,0," \o");
  912.     END;
  913.    ELSE
  914.     MyError("in XStat.data",
  915.             s1,"duplicate S-line found",TRUE);
  916.    END;
  917.   ELSE
  918.    MyError("in XStat.data",
  919.            s1,"missing currency sign in S-line",TRUE);
  920.   END;
  921.  END ParseXSDataSEntry;
  922.  
  923.  
  924.  
  925. (*---------------------------------------------------------------------------
  926.   GetXStatData: Read and parse the XStat.data file
  927.   ---------------------------------------------------------------------------*)
  928. PROCEDURE GetXStatData();
  929.  VAR
  930.   s1,s: StringT;
  931.   c: CHAR;
  932.   cxsd: XSDataPtr;
  933.  BEGIN
  934.   cxsd:=NIL;
  935.   TestXStatHeader();
  936.   REPEAT
  937.    Break.TestBreak();
  938.    GetLine(s);
  939.    s1:=s;
  940.    GetHead(s,c);
  941.    CASE c OF
  942.     |"#": (* NOP *)
  943.     |"N": ParseXSDataNEntry(s,s1,cxsd);
  944.     |"C": ParseXSDataCEntry(s,s1,cxsd);
  945.     |"S": ParseXSDataSEntry(s,s1,cxsd);
  946.     |ELSE (* unknown: NOP *)
  947.    END;
  948.   UNTIL inf.eof;
  949.  END GetXStatData;
  950.  
  951.  
  952.  
  953. (*---------------------------------------------------------------------------
  954.   ParseLog: Parse a two-line connection data entry from Xferstat
  955.   ---------------------------------------------------------------------------*)
  956. (*$ CopyDyn:=FALSE *)
  957. PROCEDURE ParseLog(line1,line2: StringT; VAR log: Connect): BOOLEAN;
  958.  VAR
  959.   c: CHAR;
  960.   l1,l2: StringT;
  961.   dummy: DosD.Date;
  962.  BEGIN
  963.   l1:=line1;
  964.   l2:=line2;
  965.   GetHead(l1,c);
  966.   log.callout:=(c="<");
  967.  
  968.   IF Get(l1,log.host) THEN
  969.    IF (log.host[0]>="0") AND (log.host[0]<="9") THEN
  970.     MyError("invalid log entry: can't find host name",line1,line2,FALSE);
  971.     RETURN FALSE;
  972.    END;
  973.    IF GetTime(l1,log.start,log.sdate) THEN
  974.     Skip(l1);
  975.     IF GetTime(l1,log.stop,dummy) THEN
  976.      GetHead(l2,c);
  977.      IF c="|" THEN
  978.       Skip(l2);
  979.       Skip(l2);
  980.       Skip(l2);
  981.       IF GetLNum(l2,log.inbb) THEN
  982.        IF GetLNum(l2,log.outbb) THEN
  983.         Skip(l2);
  984.         IF GetLNum(l2,log.inbn) THEN
  985.          IF GetLNum(l2,log.outbn) THEN
  986.           RETURN TRUE;
  987.          ELSE
  988.           MyError("invalid log entry: can't get netto send bytes",line1,line2,FALSE);
  989.          END;
  990.         ELSE
  991.          MyError("invalid log entry: can't get netto receive bytes",line1,line2,FALSE);
  992.         END;
  993.        ELSE
  994.         MyError("invalid log entry: can't get brutto send bytes",line1,line2,FALSE);
  995.        END;
  996.       ELSE
  997.        MyError("invalid log entry: can't get brutto receive bytes",line1,line2,FALSE);
  998.       END;
  999.      ELSE
  1000.       MyError("invalid log entry: illegal continuation line header ",line1,line2,FALSE);
  1001.      END;
  1002.     ELSE
  1003.      MyError("invalid log entry: can't compute end time",line1,line2,FALSE);
  1004.     END;
  1005.    ELSE
  1006.     MyError("invalid log entry: can't compute start time",line1,line2,FALSE);
  1007.    END;
  1008.   ELSE
  1009.    MyError("invalid log entry: can't find host name",line1,line2,FALSE);
  1010.   END;
  1011.   RETURN FALSE;
  1012.  END ParseLog;
  1013.  
  1014.  
  1015.  
  1016. (*---------------------------------------------------------------------------
  1017.   ComputeCost: Compute the cost for a single connect
  1018.   ---------------------------------------------------------------------------*)
  1019. PROCEDURE ComputeCost(VAR log: Connect): BOOLEAN;
  1020.  VAR
  1021.   cxsd: XSDataPtr;
  1022.   ccost: CostPtr;
  1023.   time: REAL;
  1024.  BEGIN
  1025.   log.seconds:=log.stop-log.start+1;
  1026.   IF log.seconds<0 THEN
  1027.     log.seconds:=log.seconds+7*dmult;
  1028.   END;
  1029.  
  1030.   cxsd:=xsroot;
  1031.   WHILE (cxsd#NIL) AND (String.Compare(cxsd^.host,log.host)#0) DO
  1032.     Break.TestBreak();
  1033.     cxsd:=cxsd^.next;
  1034.   END;
  1035.   IF cxsd=NIL THEN
  1036.     MyError("ERROR: no connection cost data found for host",log.host,
  1037.             "extend your XStat.data file!",FALSE);
  1038.     RETURN FALSE;
  1039.   END;
  1040.  
  1041.   IF NOT (cxsd^.local OR cxsd^.ignore) THEN
  1042.     ccost:=cxsd^.costlist;
  1043.     WHILE log.start>ccost^.stop DO
  1044.       Break.TestBreak();
  1045.       ccost:=ccost^.next;
  1046.     END;
  1047.   END;
  1048.  
  1049.   IF log.stop<log.start THEN
  1050.     log.stop:=log.stop+7*dmult;
  1051.   END;
  1052.   log.cost:=0.0;
  1053.   log.units:=0;
  1054.   IF NOT (cxsd^.local OR cxsd^.ignore) THEN
  1055.     time:=REAL(log.start);
  1056.     REPEAT
  1057.       Break.TestBreak();
  1058.       log.cost:=log.cost+ccost^.mpu;
  1059.       time:=time+ccost^.unit;
  1060.       INC(log.units);
  1061.  
  1062.       IF LONGINT(time)>ccost^.stop THEN
  1063.         ccost:=ccost^.next;
  1064.         IF ccost=NIL THEN
  1065.           ccost:=cxsd^.costlist;
  1066.           time:=time-REAL(7*dmult);
  1067.           log.stop:=log.stop-7*dmult;
  1068.         END;
  1069.       END;
  1070.     UNTIL LONGINT(time)>log.stop;
  1071.   END;
  1072.   log.local:=cxsd^.local;
  1073.   log.ignore:=cxsd^.ignore;
  1074.   RETURN TRUE;
  1075.  END ComputeCost;
  1076.  
  1077.  
  1078.  
  1079. (*---------------------------------------------------------------------------
  1080.   DisplayConnect: Display the statistics for a single connect
  1081.   ---------------------------------------------------------------------------*)
  1082. PROCEDURE DisplayConnect(log: Connect);
  1083.  VAR
  1084.   name: StringT;
  1085.  BEGIN
  1086.   name:=log.host;
  1087.   WHILE String.Length(name)<8 DO
  1088.    Break.TestBreak();
  1089.    String.Insert(name,0," \o");
  1090.   END;
  1091.   InOut.WriteString("\nhost name\t"); InOut.WriteString(name);
  1092.   InOut.WriteString("\ndirection\t     "); IF log.callout THEN
  1093.                                             InOut.WriteString("out");
  1094.                                            ELSE
  1095.                                             InOut.WriteString(" in");
  1096.                                            END;
  1097.   InOut.WriteString("\nonline time\t"); InOut.WriteInt(log.seconds,8); InOut.WriteString(" seconds");
  1098.   InOut.WriteString("\nunits\t\t");
  1099.   IF log.local THEN
  1100.     InOut.WriteString("LOCAL");
  1101.   ELSIF log.ignore THEN
  1102.     InOut.WriteString("IGNORE");
  1103.   ELSE
  1104.     InOut.WriteInt(log.units,8); InOut.WriteString(" units");
  1105.   END;
  1106.   InOut.WriteString("\ncost\t\t");
  1107.   IF log.local THEN
  1108.     InOut.WriteString("LOCAL");
  1109.   ELSIF log.ignore THEN
  1110.     InOut.WriteString("IGNORE");
  1111.   ELSE
  1112.     RealInOut.WriteReal(log.cost,8,2); InOut.Write(" "); InOut.WriteString(currency);
  1113.   END;
  1114.   InOut.WriteString("\n\n\n");
  1115.  END DisplayConnect;
  1116.  
  1117.  
  1118.  
  1119. (*---------------------------------------------------------------------------
  1120.   DisplayStats: Display the overall statistics
  1121.   ---------------------------------------------------------------------------*)
  1122. PROCEDURE DisplayStats(tot: Statistics);
  1123.  BEGIN
  1124.   InOut.WriteString("Connection statistics for ");
  1125.   IF NOT tot.in THEN
  1126.    InOut.WriteString("outgoing");
  1127.   ELSE
  1128.    InOut.WriteString("incoming");
  1129.   END;
  1130.   IF tot.name[0]="\o" THEN
  1131.     InOut.WriteString(" calls:\n");
  1132.     InOut.WriteString("-----------------------------------------\n");
  1133.   ELSE
  1134.     InOut.WriteString(" calls ");
  1135.     IF NOT tot.in THEN
  1136.       InOut.WriteString("to");
  1137.     ELSE
  1138.       InOut.WriteString("from");
  1139.     END;
  1140.     InOut.WriteString(' host "');
  1141.     InOut.WriteString(tot.name);
  1142.     InOut.WriteString('":\n');
  1143.   END;
  1144.  
  1145.   IF tot.connects=0 THEN
  1146.    InOut.WriteString("\nno connects recorded.\n\n\n");
  1147.   ELSE
  1148.    InOut.WriteString("\nconnects        "); InOut.WriteInt(tot.connects,8); InOut.WriteString("  \t  ("); InOut.WriteInt(tot.localconnects,0); InOut.WriteString(" of them were local)");
  1149.    IF tot.name[0]="\o" THEN
  1150.      InOut.WriteString("\nignored connects"); InOut.WriteInt(tot.ignoreconnects,8);
  1151.    END;
  1152.    InOut.WriteString("\nonline time     "); InOut.WriteInt(tot.seconds,8); InOut.WriteString(" sec\t  ("); InOut.WriteInt((tot.seconds+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" sec/connect)");
  1153.    InOut.WriteString("\nunits           "); InOut.WriteInt(tot.units,8); InOut.WriteString(" units\t  ("); RealInOut.WriteReal(REAL(tot.units)/REAL(tot.connects),8,3); InOut.WriteString(" units/connect)");
  1154.    InOut.WriteString("\ncost            "); RealInOut.WriteReal(tot.cost,8,2); InOut.WriteString(currency); InOut.WriteString("\t  ("); RealInOut.WriteReal(tot.cost/REAL(tot.connects),8,3); InOut.WriteString(currency); InOut.WriteString("/connect)");
  1155.  InOut.WriteString("\n\nbrutto read     "); InOut.WriteInt(tot.inbb,8); InOut.WriteString(" bytes\t  ("); InOut.WriteInt((tot.inbb+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)");
  1156.    InOut.WriteString("\nbrutto send     "); InOut.WriteInt(tot.outbb,8); InOut.WriteString(" bytes\t  ("); InOut.WriteInt((tot.outbb+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)");
  1157.    InOut.WriteString("\nnetto read      "); InOut.WriteInt(tot.inbn,8); InOut.WriteString(" bytes\t  ("); InOut.WriteInt((tot.inbn+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)");
  1158.    InOut.WriteString("\nnetto send      "); InOut.WriteInt(tot.outbn,8); InOut.WriteString(" bytes\t  (");  InOut.WriteInt((tot.outbn+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)\n");
  1159.    InOut.WriteString("\nav. brutto speed"); InOut.WriteInt((tot.inbb+tot.outbb) DIV tot.seconds,8); InOut.WriteString(" cps\t  ("); InOut.WriteInt(tot.bpeak,8); InOut.WriteString(" cps peak)");
  1160.    InOut.WriteString("\nav. netto speed "); InOut.WriteInt((tot.inbn+tot.outbn) DIV tot.seconds,8); InOut.WriteString(" cps\t  ("); InOut.WriteInt(tot.npeak,8); InOut.WriteString(" cps peak)");
  1161.  
  1162.    InOut.WriteString("\nav. brutto cost ");
  1163.    IF tot.inbb+tot.outbb=0 THEN
  1164.     InOut.WriteString("       -");
  1165.    ELSE
  1166.     RealInOut.WriteReal(tot.cost/REAL(tot.inbb+tot.outbb)*1048576.0,8,3);
  1167.    END;
  1168.    InOut.WriteString(currency); InOut.WriteString("/MB");
  1169.  
  1170.    InOut.WriteString("\nav. netto cost  ");
  1171.    IF tot.inbn+tot.outbn=0 THEN
  1172.     InOut.WriteString("       -");
  1173.    ELSE
  1174.     RealInOut.WriteReal(tot.cost/REAL(tot.inbn+tot.outbn)*1048576.0,8,3);
  1175.    END;
  1176.    InOut.WriteString(currency); InOut.WriteString("/MB\n\n\n");
  1177.   END;
  1178.  END DisplayStats;
  1179.  
  1180.  
  1181.  
  1182. (*---------------------------------------------------------------------------
  1183.   UpdateStat: Update the totali/totalo records
  1184.   ---------------------------------------------------------------------------*)
  1185. PROCEDURE UpdateStat(VAR tot: Statistics; VAR log: Connect);
  1186.  VAR
  1187.   tempspeed: LONGINT;
  1188.  BEGIN
  1189.   IF NOT log.ignore THEN
  1190.     INC(tot.connects);
  1191.     tot.seconds:=tot.seconds+log.seconds;
  1192.     tot.inbb:=tot.inbb+log.inbb;
  1193.     tot.outbb:=tot.outbb+log.outbb;
  1194.     tot.inbn:=tot.inbn+log.inbn;
  1195.     tot.outbn:=tot.outbn+log.outbn;
  1196.     tot.units:=tot.units+log.units;
  1197.     tot.cost:=tot.cost+log.cost;
  1198.  
  1199.     IF log.seconds#0 THEN
  1200.      tempspeed:=(log.inbb+log.outbb) DIV log.seconds;
  1201.      IF tempspeed>tot.bpeak THEN tot.bpeak:=tempspeed END;
  1202.  
  1203.      tempspeed:=(log.inbn+log.outbn) DIV log.seconds;
  1204.      IF tempspeed>tot.npeak THEN tot.npeak:=tempspeed END;
  1205.     END;
  1206.   END;
  1207.   IF log.ignore THEN
  1208.     INC(tot.ignoreconnects);
  1209.   END;
  1210.   IF log.local THEN
  1211.     INC(tot.localconnects);
  1212.   END;
  1213.  END UpdateStat;
  1214.  
  1215.  
  1216.  
  1217. (*---------------------------------------------------------------------------
  1218.   ClearStat: Preset a Statistics-record with zeros
  1219.   ---------------------------------------------------------------------------*)
  1220. PROCEDURE ClearStat(VAR stat: Statistics);
  1221.   BEGIN
  1222.     stat.next:=NIL;
  1223.     stat.name:="\o\o";
  1224.     stat.in:=FALSE;
  1225.     stat.connects:=0;
  1226.     stat.inbb:=0;
  1227.     stat.outbb:=0;
  1228.     stat.inbn:=0;
  1229.     stat.outbn:=0;
  1230.     stat.cost:=0.0;
  1231.     stat.units:=0;
  1232.     stat.seconds:=0;
  1233.     stat.bpeak:=0;
  1234.     stat.npeak:=0;
  1235.     stat.localconnects:=0;
  1236.     stat.ignoreconnects:=0;
  1237.   END ClearStat;
  1238.  
  1239.  
  1240.  
  1241. (*---------------------------------------------------------------------------
  1242.   InsertStat: Insert a Statistics-record at the proper position
  1243.               into the linked list
  1244.   ---------------------------------------------------------------------------*)
  1245. PROCEDURE InsertStat(stat: StatisticsPtr);
  1246.   VAR
  1247.     cur,prev: StatisticsPtr;
  1248.     done: BOOLEAN;
  1249.   BEGIN
  1250.     done:=FALSE;
  1251.     IF statroot=NIL THEN
  1252.       statroot:=stat;
  1253.     ELSE
  1254.       cur:=statroot;
  1255.       prev:=NIL;
  1256.       REPEAT
  1257.         IF String.Compare(stat^.name,cur^.name)=0 THEN
  1258.           IF stat^.in THEN
  1259.             (* insert before current *)
  1260.             IF prev=NIL THEN
  1261.               stat^.next:=statroot;
  1262.               statroot:=stat;
  1263.               done:=TRUE;
  1264.             ELSE
  1265.               stat^.next:=prev^.next;
  1266.               prev^.next:=stat;
  1267.               done:=TRUE;
  1268.             END;
  1269.           ELSE
  1270.             (* insert after current *)
  1271.             stat^.next:=cur^.next;
  1272.             cur^.next:=stat;
  1273.             done:=TRUE;
  1274.           END;
  1275.         ELSIF String.Compare(stat^.name,cur^.name)<0 THEN
  1276.           (* insert before current *)
  1277.           IF prev=NIL THEN
  1278.             stat^.next:=statroot;
  1279.             statroot:=stat;
  1280.             done:=TRUE;
  1281.           ELSE
  1282.             stat^.next:=prev^.next;
  1283.             prev^.next:=stat;
  1284.             done:=TRUE;
  1285.           END;
  1286.         ELSE
  1287.           prev:=cur;
  1288.           cur:=cur^.next;
  1289.         END;
  1290.         Break.TestBreak();
  1291.       UNTIL (cur=NIL) OR done;
  1292.       IF cur=NIL THEN
  1293.         prev^.next:=stat;
  1294.       END;
  1295.     END;
  1296.   END InsertStat;
  1297.  
  1298.  
  1299.  
  1300. (*---------------------------------------------------------------------------
  1301.   FindStat: Search linked list for a specific Statistics-record
  1302.             Create that record if not found
  1303.   ---------------------------------------------------------------------------*)
  1304. PROCEDURE FindStat(name: StringT; in: BOOLEAN): StatisticsPtr;
  1305.   VAR
  1306.     cur,next: StatisticsPtr;
  1307.     found: BOOLEAN;
  1308.   BEGIN
  1309.     next:=statroot;
  1310.     IF next#NIL THEN
  1311.       REPEAT
  1312.         cur:=next;
  1313.         next:=cur^.next;
  1314.         found:=(in=cur^.in) AND (String.Compare(name,cur^.name)=0);
  1315.         Break.TestBreak();
  1316.       UNTIL found OR (next=NIL);
  1317.     ELSE
  1318.       found:=FALSE;
  1319.     END;
  1320.     IF found THEN
  1321.       RETURN cur;
  1322.     ELSE
  1323.       next:=ExecL.AllocMem(SIZE(Statistics),ExecD.MemReqSet{});
  1324.       IF next=NIL THEN
  1325.         MyError("","not enough memory","",TRUE);
  1326.       END;
  1327.       ClearStat(next^);
  1328.       next^.name:=name;
  1329.       next^.in:=in;
  1330.       InsertStat(next);
  1331.       RETURN next;
  1332.     END;
  1333.   END FindStat;
  1334.  
  1335.  
  1336.  
  1337. (*---------------------------------------------------------------------------
  1338.   DisplayStatList: Display the entire list of Statistics-records
  1339.   ---------------------------------------------------------------------------*)
  1340. PROCEDURE DisplayStatList;
  1341.   VAR
  1342.     cur: StatisticsPtr;
  1343.   BEGIN
  1344.     cur:=statroot;
  1345.     WHILE cur#NIL DO
  1346.       IF (cur^.in AND args.in) OR ((NOT cur^.in) AND args.out) THEN
  1347.         DisplayStats(cur^);
  1348.       END;
  1349.       cur:=cur^.next;
  1350.     END;
  1351.   END DisplayStatList;
  1352.  
  1353.  
  1354.  
  1355. BEGIN
  1356.  SYSTEM.SETREG(11,SYSTEM.ADR(version));
  1357.  rdargsPtr:=NIL;
  1358.  xsroot:=NIL;
  1359.  statroot:=NIL;
  1360.  inf.fh:=NIL;
  1361.  stdErr:=NIL;
  1362.  
  1363.  IF (Arts.kickVersion<37) OR (DosL.dosVersion<37) THEN
  1364.   MyError("","You need OS 2.04 or later (dos.library V37 or later)",
  1365.           "",TRUE);
  1366.  END;
  1367.  
  1368.  Break.InstallException;
  1369.  PresetArgs();
  1370.  
  1371.  InOut.WriteString("\n\n"+vers+"\n");
  1372.  InOut.WriteString("⌐ Copyright 1992 by Jⁿrgen Weinelt\n");
  1373. (*$ IF BETA *)
  1374.  InOut.WriteString("THIS IS A BETA VERSION! DO NOT DISTRIBUTE!\n\n");
  1375. (*$ ELSE *)
  1376.  InOut.WriteString("XStat is Freeware - read the docs for details.\n\n");
  1377. (*$ ENDIF *)
  1378.  
  1379.  IF NOT Arts.wbStarted THEN
  1380.   IF Arts.dosCmdLen>0 THEN
  1381.    temp:=SYSTEM.CAST(StringTPtr,Arts.dosCmdBuf)^;
  1382.    IF temp[0]="?" THEN
  1383.     InOut.WriteString(extendedhelp);
  1384.     Arts.Exit(5);
  1385.    END;
  1386.   END;
  1387.  END;
  1388.  
  1389.  IF DosL.GetVar(SYSTEM.ADR("XSTATARGS"),SYSTEM.ADR(temp),
  1390.                 strlen,DosD.VarFlagSet{})#-1 THEN
  1391.   index:=String.Length(temp);
  1392.   IF temp[index-1]#"\n" THEN
  1393.     temp[index]:="\n"; temp[index+1]:="\o";
  1394.   END;
  1395.   ParseDosArgs(temp);
  1396.  END;
  1397.  
  1398.  IF NOT Arts.wbStarted THEN
  1399.   IF Arts.dosCmdLen>0 THEN
  1400.    temp:=SYSTEM.CAST(StringTPtr,Arts.dosCmdBuf)^;
  1401.    ParseDosArgs(temp);
  1402.   END;
  1403.  END;
  1404.  
  1405.  IF DosL.CompareDates(SYSTEM.ADR(args.from),SYSTEM.ADR(args.to))<0 THEN
  1406.   MyError("","illegal combination of parameters",
  1407.           "FROMDATE is later than TODATE",TRUE);
  1408.  END;
  1409.  IF NOT (args.in OR args.out) THEN
  1410.   MyError("","illegal combination of parameters",
  1411.           "you can't specify NOINCOM and NOOUTGO simultaneously",TRUE);
  1412.  END;
  1413.  
  1414.  IF args.stderr THEN
  1415.    stdErr:=DosL.Open(SYSTEM.ADR("CONSOLE:"),DosD.newFile);
  1416.    IF stdErr=NIL THEN
  1417.      MyError("ERROR:","can't open CONSOLE: for StdErr output",
  1418.              "guess you'll have to live without it :-)",FALSE);
  1419.    END;
  1420.  END;
  1421.  
  1422.  ClearStat(totali);
  1423.  totali.in:=TRUE;
  1424.  ClearStat(totalo);
  1425.  totalo.in:=FALSE;
  1426.  
  1427.  inf.fh:=DosL.Open(SYSTEM.ADR(args.xstatdata),DosD.readOnly);
  1428.  IF inf.fh=NIL THEN
  1429.   MyError("Can't open XStat.data!","filename was",args.xstatdata,TRUE);
  1430.  END;
  1431.  GetXStatData();
  1432.  DosL.Close(inf.fh);
  1433.  IF currency[0]=ASCII.nul THEN
  1434.   MyError("WARNING","no currency definition found in XStat.data",
  1435.           "using 'DM' as default",FALSE);
  1436.   currency:=" DM";
  1437.  END;
  1438.  
  1439.  inf.fh:=DosL.Open(SYSTEM.ADR(args.xferstat),DosD.readOnly);
  1440.  IF inf.fh=NIL THEN
  1441.   MyError("Can't open Xferstat!","filename was",args.xferstat,TRUE);
  1442.  END;
  1443.  
  1444.  REPEAT
  1445.   Break.TestBreak();
  1446.   GetLog(ln1,ln2);
  1447.   IF String.Length(ln1)+String.Length(ln2)>0 THEN
  1448.    IF ParseLog(ln1,ln2,log) THEN
  1449.     IF (args.host[0]=ASCII.nul) OR
  1450.        (DosL.MatchPatternNoCase(SYSTEM.ADR(args.host),SYSTEM.ADR(log.host))) THEN
  1451.      IF (DosL.CompareDates(SYSTEM.ADR(args.from),SYSTEM.ADR(log.sdate))>=0) AND
  1452.         (DosL.CompareDates(SYSTEM.ADR(log.sdate),SYSTEM.ADR(args.to))>=0) THEN
  1453.       IF ComputeCost(log) THEN
  1454.        IF args.verbose THEN
  1455.         DisplayConnect(log);
  1456.        END;
  1457.  
  1458.        IF args.perhost THEN
  1459.          IF NOT log.ignore THEN
  1460.            UpdateStat(FindStat(log.host,NOT log.callout)^,log);
  1461.          END;
  1462.        END;
  1463.        IF log.callout THEN
  1464.          UpdateStat(totalo,log);
  1465.        ELSE
  1466.          UpdateStat(totali,log);
  1467.        END;
  1468.  
  1469.       ELSE
  1470.        IF NOT args.quiet THEN
  1471.         InOut.WriteString("(ignoring this one)\n\n\n");
  1472.        END;
  1473.       END;
  1474.      END;
  1475.     END;
  1476.    ELSE
  1477.     IF NOT args.quiet THEN
  1478.      InOut.WriteString("(ignoring this one)\n\n\n");
  1479.     END;
  1480.    END;
  1481.   END;
  1482.  UNTIL inf.eof;
  1483.  
  1484.  IF args.perhost THEN
  1485.    DisplayStatList;
  1486.  END;
  1487.  IF args.in THEN
  1488.   DisplayStats(totali);
  1489.  END;
  1490.  IF args.out THEN
  1491.   DisplayStats(totalo);
  1492.  END;
  1493. CLOSE
  1494.   IF rdargsPtr#NIL THEN
  1495.     DosL.FreeArgs(rdargsPtr);
  1496.     DosL.FreeDosObject(DosD.dosRdArgs,rdargsPtr);
  1497.     rdargsPtr:=NIL;
  1498.   END;
  1499.   IF xsroot#NIL THEN
  1500.     FreeXData(xsroot);
  1501.     xsroot:=NIL;
  1502.   END;
  1503.   IF statroot#NIL THEN
  1504.     FreeStat(statroot);
  1505.     statroot:=NIL;
  1506.   END;
  1507.   IF inf.fh#NIL THEN
  1508.     DosL.Close(inf.fh);
  1509.     inf.fh:=NIL;
  1510.   END;
  1511.   IF stdErr#NIL THEN
  1512.     DosL.Close(stdErr);
  1513.     stdErr:=NIL;
  1514.   END;
  1515. END XStat.
  1516.